package algorithms.timewheel;

import jdk.jdk8.localdate.LocalDateTimeUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @description: 时间轮算法1:秒级时间论60个时间格子,每个格子代表1s
 * @author: zk
 * @date: 2023-04-14 16:46
 */
public class TimeWheelSimple {
    private static final int SIZE = 60;
    private Thread timeWheelSimple;

    private ThreadPoolExecutor handlerPoolExecutor;

    private TimeWheelSimple() {
    }

    // 单例
    public static final TimeWheelSimple INSTANCE = new TimeWheelSimple();

    // 时间论-格子
    private Map<Integer, List<Handler>> timeWheel = new ConcurrentHashMap(SIZE);

    private String currTime() {
        return LocalDateTimeUtils.format(LocalDateTime.now(), LocalDateTimeUtils.DEFAULT_DATE_PATTERN) + ": ";
    }

    public synchronized void addHandler(Handler handler,long time) {
//        long futureTime = System.currentTimeMillis() + handler.getTime() * 1000;
        long posi = time % (SIZE -1);
        timeWheel.get((int) posi).add(handler);
        //System.out.println(currTime() + "新增任务-posi=" + posi + ",handler=" + handler.toString());
    }

    public void start() {
        // 初始化时间论
        timeWheelInit();

        // 任务执行线程池
        initHandlerPoolExecutor();

        // 开启时间论
        timeWheelStart();
    }

    private void initHandlerPoolExecutor() {
        System.out.println(currTime() + "任务执行线程池已经启动。。。");
        handlerPoolExecutor = new ThreadPoolExecutor(5,
                100,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(1000),
                new ThreadPoolExecutor.CallerRunsPolicy());
    }

    private void timeWheelInit() {
        System.out.println(currTime() + "初始化时间论。。。");
        for (int i = 0; i < SIZE; i++) {
            timeWheel.put(i, new ArrayList<>());
        }
        System.out.println(currTime() + "初始化成功");
    }

    private void timeWheelStart() {
        timeWheelSimple = new Thread(() -> {
            while (true) {
                timeWheel.forEach((sec, handlers) -> {
                    // 处理当前时间格的任务
                    if (isNotEmpty(handlers)) {
                        // System.out.println(currTime() + "开始执行任务=handlers"+handlers);
                        List<Handler> repeatList = new ArrayList<>();
                        for (Handler handler : handlers) {
                            // 交给线程池处理
                            handlerPoolExecutor.execute(() -> exec(handler));

                            if(!handler.getOnce()) {
                                repeatList.add(handler);
                            }

                        }

                        // 更新
                        handlers.clear();

                        // 任务重入
                        if (isNotEmpty(repeatList)) {
                            repeatList.stream().forEach(handler -> {
                                addHandler(handler,handler.getTime() + sec);
                            });
                        }
                    }

                    // 延迟1s
                    try {
//                        TimeUnit.MILLISECONDS.sleep(1000 - System.currentTimeMillis() % 1000);
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                });
            }
        });

        timeWheelSimple.start();
        System.out.println(currTime() + "时间论启动成功。。。");
    }

    private boolean isNotEmpty(Collection collection) {
        return collection != null && collection.size() > 0;
    }

    private boolean isEmpty(Collection collection) {
        return !isNotEmpty(collection);
    }

    private void exec(Handler handler) {
        System.out.println(currTime() + "执行任务=" + handler.getName());
    }


    static class Handler {
        private String name;
        private Long time;
        private Boolean once = true;


        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Long getTime() {
            return time;
        }

        public void setTime(Long time) {
            this.time = time;
        }

        public Boolean getOnce() {
            return once;
        }

        public void setOnce(Boolean once) {
            this.once = once;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Handler handler = (Handler) o;
            return Objects.equals(name, handler.name) && Objects.equals(time, handler.time) && Objects.equals(once, handler.once);
        }

        @Override
        public int hashCode() {
            return Objects.hash(name, time, once);
        }

        @Override
        public String toString() {
            return "Handler{" +
                    "name='" + name + '\'' +
                    ", time=" + time +
                    ", once=" + once +
                    '}';
        }
    }

}
